using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Hashtable = System.Collections.Hashtable;

namespace Framework
{
    public static class DictionaryUtil
    {
        // *********************************************************************************
        // Hashtable converters
        // *********************************************************************************

        #region KeyValueArrayAsDictionary
        public static IDictionary<string, string> KeyValueArrayAsDictionary(params string[] keyValues)
        {
            if (keyValues.Length % 2 == 1)
            {
                throw new Exception("KeyValueArrayToHashTable - Must provide an even number of elements");
            }

            var result = new Dictionary<string, string>(keyValues.Length / 2, FrameworkConstant.DEFAULT_COMPARER_IGNORECASE);

            for (var i = 0; i < keyValues.Length; i += 2)
            {
                result.Add(keyValues[i], keyValues[i + 1]);
            }

            return result;
        }
        #endregion

        #region KeyValueArrayAsKeyValueList
        public static List<KeyValuePair<string, string>> KeyValueArrayAsKeyValueList(params string[] keyValues)
        {
            if (keyValues.Length % 2 == 1)
            {
                throw new Exception("KeyValueArrayAsKeyValueList - Must provide an even number of elements");
            }

            var result = new List<KeyValuePair<string, string>>(keyValues.Length / 2);

            for (var i = 0; i < keyValues.Length; i += 2)
            {
                result.Add(new KeyValuePair<string, string>(keyValues[i], keyValues[i + 1]));
            }

            return result;
        }
        #endregion

        #region GenericDictionaryToHashtable
        public static IDictionary GenericDictionaryToHashtable(IDictionary<string, string> values)
        {
            var result = new Hashtable();

            foreach (var key in values.Keys)
            {
                result.Add(key, values[key]);
            }

            return result;
        }
        #endregion

        #region ReflectedPropertiesToHashtable
        public static IDictionary ReflectedPropertiesToHashtable(object instance)
        {
            // Consider using route dictionary for this
            var result = new Hashtable();

            foreach (var propInfo in ReflectUtil.GetProperties(instance))
            {
                result.Add(propInfo.Name, ReflectUtil.GetPropertyOrFieldValue(instance, propInfo.Name));
            }

            return result;
        }
        #endregion

        #region ReflectedPropertiesToKeyValueList
        public static List<KeyValuePair<string, object>> ReflectedPropertiesToKeyValueList(object instance)
        {
            return
                ReflectUtil.GetProperties(instance).Select(
                    propInfo => new KeyValuePair<string, object>(propInfo.Name, ReflectUtil.GetPropertyOrFieldValue(instance, propInfo.Name))).ToList();
        }

        public static List<KeyValuePair<string, object>> ReflectedPropertiesToKeyValueList(object instance, string namePrefix)
        {
            return
                ReflectUtil.GetProperties(instance).Select(
                    propInfo => new KeyValuePair<string, object>(namePrefix + propInfo.Name, ReflectUtil.GetPropertyOrFieldValue(instance, propInfo.Name))).ToList();
        }
        #endregion

        // *********************************************************************************
        // Read property file (Name/Values) into a dictionary
        // *********************************************************************************

        #region ReadNameValuesFromFile
        public static IDictionary<string, string> ReadNameValuesFromFile(string nameValuesFile)
        {
            var result = new Dictionary<string, string>(FrameworkConstant.DEFAULT_COMPARER_IGNORECASE);

            var lines = ReadNameValueContentLines(nameValuesFile);

            foreach (var line in lines)
            {
                var key = StrUtil.LeftIndexOf(line, "|").Trim();
                var value = StrUtil.RightIndexOf(line, "|").Trim();

                if (Is.NotEmptyString(key) && !result.ContainsKey(key))
                {
                    result.Add(key, value);
                }
            }

            return result;
        }
        #endregion

        #region ReadNameValueContentLines
        private static List<string> ReadNameValueContentLines(string nameValuesFile)
        {
            var lines = File.ReadAllLines(nameValuesFile);

            var result = new List<string>();
            var vertical = String.Empty;

            foreach (var line in lines)
            {
                var value = line.Trim();

                // Comment - Skip It
                if (StrUtil.StartsWith(line, "#") || Is.EmptyString(value))
                {
                    continue;
                }

                result.Add(value);
            }

            return result;
        }
        #endregion

        // *********************************************************************************
        // Read IDictionary<string, string> as a null / type safe value
        // *********************************************************************************

        #region GetValue (Get Dictionary Values as String, Bool, Int)
        /// <summary>
        /// Retreive dictionary value, if the dictionary value is null or empty string then return <var>String.Empty</var>.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static string GetValue(IDictionary<string, string> dictionary, string key)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return StrUtil.DefaultValue(value);
        }

        /// <summary>
        /// Retreive dictionary value, if the dictionary value is null or empty string then return<var>defaultValue</var>.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static string GetValue(IDictionary<string, string> dictionary, string key, string defaultValue)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return StrUtil.DefaultValue(value, defaultValue);
        }

        /// <summary>
        /// Retreive dictionary value as date time.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static DateTime GetValueAsDateTime(IDictionary<string, string> dictionary, string key)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return DateUtil.ToDateTime(value);
        }

        /// <summary>
        /// Retreive dictionary value as int.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static int GetValueAsInt(IDictionary<string, string> dictionary, string key)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return NumUtil.ParseInt(value);
        }

        /// <summary>
        /// Retreive dictionary value as int, if the dictionary valueis null or does not parse as an int then return<var>defaultValue</var>.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static int GetValueAsInt(IDictionary<string, string> dictionary, string key, int defaultValue)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return NumUtil.ParseInt(value, defaultValue);
        }

        /// <summary>
        /// Retreive dictionary value as decimal.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static decimal GetValueAsDecimal(IDictionary<string, string> dictionary, string key)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return NumUtil.ParseDecimal(value);
        }

        /// <summary>
        /// Retreive dictionary value as decimal, if the dictionary valueis null or does not parse as an decimal then return<var>defaultValue</var>.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">The default value.</param>
        /// <returns></returns>
        public static decimal GetValueAsDecimal(IDictionary<string, string> dictionary, string key, decimal defaultValue)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return NumUtil.ParseDecimal(value, defaultValue);
        }

        /// <summary>
        /// Retreive dictionary value as bool.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public static bool GetValueAsBool(IDictionary<string, string> dictionary, string key)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return Is.True(value);
        }

        /// <summary>
        /// Retreive dictionary value as bool, if the dictionary valueis null or does not parse as a bool then return<var>defaultValue</var>.
        /// </summary>
        /// <param name="dictionary">The dictionary.</param>
        /// <param name="key">The key.</param>
        /// <param name="defaultValue">if set to <c>true</c> [default value].</param>
        /// <returns></returns>
        public static bool GetValueAsBool(IDictionary<string, string> dictionary, string key, bool defaultValue)
        {
            var value = dictionary.ContainsKey(key) ? dictionary[key] : string.Empty;

            return Is.EmptyString(value) ? defaultValue : Is.True(value);
        }
        #endregion

        // *********************************************************************************
        // Other
        // *********************************************************************************

        /// <summary>
        /// Joins the specified two dictionaries, .
        /// </summary>
        /// <typeparam name="TKey">The type of the key.</typeparam>
        /// <typeparam name="TValue">The type of the value.</typeparam>
        /// <param name="primaryDictionary">The primary dictionary.</param>
        /// <param name="extraDictionary">The RHS.</param>
        public static void Join<TKey, TValue>(IDictionary<TKey, TValue> primaryDictionary, IDictionary<TKey, TValue> extraDictionary)
        {
            foreach (var kv in extraDictionary)
            {
                primaryDictionary.Add(kv.Key, kv.Value);
            }
        }
    }
}